/**
 * 
 */
package chui;

import java.io.IOException;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;

import model.Message;
import model.Observation;
import model.Position;
import model.RemoteZone;
import model.ZoneRelay;
import util.ListeningServer;
import util.SimpleLog;

import comm.UdpCommsPackage;

import constants.EObservationType;
import fileio.FileIo;


/**
 * [Work in process]
 * 
 * @author rpitts@gmu.edu
 *
 */
public class ZoneMonitor extends Observable implements Observer, Runnable{

	private UdpCommsPackage comms;
	private ZoneRelay zone;
	private String name = "ZoneRelay";
	private FileIo fileIo;
	//private SimpleLog sLog; 

	ZoneMonitor(ZoneRelay zone, String basePath, int mean) throws Exception{
		comms = new UdpCommsPackage(zone.getName());
		this.zone = zone;
		this.fileIo = new FileIo(basePath + "/"
				+ (int) mean + "-" + zone.getWindowSize() + "-"
				+ zone.getName()+"-" + "ZoneMonitor.csv");

		this.run();
	}
	
	@Override
	public void run() {
		
		

		comms.addObserver(this);

		writeMessageToFile("Starting," + this.name + "," + this.zone.getName());

		if (zone.hasRemoteZones()) {
			//ListeningServer lS = new ListeningServer(this, zone.getPort());
			new ListeningServer(this, zone.getPort());
		}
		
	}

	@Override
	public void update(Observable observable, Object object) {
		
		if (object instanceof Observation) {
			
			Observation obs = (Observation) object;
			if (obs.getLocation().getZone().equals(zone.getName())) {

				if (obs.contains(EObservationType.MEDVAC_NEEDED)) {
					sendMedVac(obs.getLocation());
				}

				writeMessageToFile("Observation, " + obs.getLocation().getZone()
						 	   + ":" + obs.getLocation().getX()
						 	   + "-" + obs.getLocation().getY() + ","
						 	   + obs.getObserver() + ","
						 	   + obs.getObservations() );
			}
			//else {
			//	writeMessageToFile("Ignoring Observation from Zone: " + obs.getLocation().getZone());
			//}
			
		} else if (object instanceof Message) {
			Message message = (Message) object;
			
			// Process Message if it's from this zone
			if (message.getZone().equals(zone.getName())) {
				writeMessageToFile("IntraZone Message Received," + message.getZone() 
						+ "," + message.getMessage() );
			}
		}
	}
	
	public void receiveRemoteMessage (Message messageIn) {
		writeMessageToFile("InterZone Message Received," + messageIn.getZone() 
			      + ", " + messageIn.getMessage() );	
		Message msg = new Message(this.zone.getName(), messageIn.getMessage());
		try {
			comms.broadcastSerializable(msg);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void Halt() throws Exception {
		Message msg = new Message(this.zone.getName(), "System Halt");
		comms.broadcastSerializable(msg);
		comms.close();
		fileIo.close();
	}

	public void quitAllNodes() throws Exception {
		Message msg = new Message(this.zone.getName(), "Quit all Nodes");
		comms.broadcastSerializable(msg);
	}
	
	public void sendMedVac(Position location) {
		Message msg;
		if (location!=null){ 
			msg = new Message(this.zone.getName(), "Request MedVac from "+zone.getName()+" ("+location.getX()+","+location.getY()+")");
		}
		else 
			msg = new Message(this.zone.getName(), "Request Simulated MedVac from "+zone.getName());
		if (zone.hasRemoteZones()) {
			Iterator<RemoteZone> itr = zone.remoteZones();
			while (itr.hasNext()) {
				
				// Get the next RemoteZone
				RemoteZone rZ = (RemoteZone) itr.next();
				
				// Send Message
				rZ.sendMessage(msg);
			}
		}

	}
	
	protected synchronized void writeMessageToFile(String message){
		//writes a single line to file using timestamp,zone,id and a message
		
		//Runtime handle--retrieve memory values, etc
		Runtime runtime = Runtime.getRuntime();
		
		fileIo.write(System.currentTimeMillis()+","
				+ zone.getName()+","
				+ runtime.freeMemory() + ","
				+ runtime.maxMemory() + ","
				+ runtime.totalMemory() + "," 
				+ message );
	}

	
}
